import { exec } from 'child_process';
import { promisify } from 'util';
import * as path from 'path';
import * as fs from 'fs-extra';

const execAsync = promisify(exec);

export interface MavenResult {
  success: boolean;
  output: string;
  error?: string;
  duration: number;
  command: string;
}

export interface MavenProjectInfo {
  groupId: string;
  artifactId: string;
  version: string;
  packaging: string;
  dependencies: Array<{
    groupId: string;
    artifactId: string;
    version: string;
    scope?: string;
  }>;
}

export class MavenExecutor {
  private readonly mavenCmd = 'cmd.exe /c "C:\\work\\apache-maven-3.9.9\\bin\\mvn.cmd';
  
  async compile(projectPath: string, clean: boolean = false): Promise<string> {
    const commands = [];
    if (clean) {
      commands.push('clean');
    }
    commands.push('compile');
    
    const result = await this.executeMavenCommand(projectPath, commands.join(' '));
    
    if (!result.success) {
      throw new Error(`Maven编译失败: ${result.error || result.output}`);
    }
    
    return this.formatMavenOutput('编译', result);
  }

  async test(projectPath: string, skipTests: boolean = false): Promise<string> {
    const command = skipTests ? 'package -DskipTests' : 'test';
    const result = await this.executeMavenCommand(projectPath, command);
    
    if (!result.success && !skipTests) {
      // 测试失败时提供详细信息
      const testResults = this.parseTestResults(result.output);
      return this.formatTestResults(result, testResults);
    }
    
    return this.formatMavenOutput(skipTests ? '打包(跳过测试)' : '测试', result);
  }

  async package(projectPath: string, skipTests: boolean = true): Promise<string> {
    const command = skipTests ? 'package -DskipTests' : 'package';
    const result = await this.executeMavenCommand(projectPath, command);
    
    if (!result.success) {
      throw new Error(`Maven打包失败: ${result.error || result.output}`);
    }
    
    return this.formatMavenOutput('打包', result);
  }

  async clean(projectPath: string): Promise<string> {
    const result = await this.executeMavenCommand(projectPath, 'clean');
    
    if (!result.success) {
      throw new Error(`Maven清理失败: ${result.error || result.output}`);
    }
    
    return this.formatMavenOutput('清理', result);
  }

  async install(projectPath: string): Promise<string> {
    const result = await this.executeMavenCommand(projectPath, 'install -DskipTests');
    
    if (!result.success) {
      throw new Error(`Maven安装失败: ${result.error || result.output}`);
    }
    
    return this.formatMavenOutput('安装到本地仓库', result);
  }

  async runSpringBoot(projectPath: string): Promise<string> {
    try {
      // 检查是否有SpringBoot插件
      const hasSpringBootPlugin = await this.hasSpringBootPlugin(projectPath);
      if (!hasSpringBootPlugin) {
        throw new Error('项目中未找到SpringBoot插件配置');
      }

      const result = await this.executeMavenCommand(projectPath, 'spring-boot:run', 30000); // 30秒超时
      return this.formatMavenOutput('SpringBoot启动', result);
    } catch (error) {
      return `SpringBoot启动失败: ${error instanceof Error ? error.message : String(error)}`;
    }
  }

  async dependencyTree(projectPath: string): Promise<string> {
    const result = await this.executeMavenCommand(projectPath, 'dependency:tree');
    
    if (!result.success) {
      throw new Error(`获取依赖树失败: ${result.error || result.output}`);
    }
    
    return this.formatMavenOutput('依赖树分析', result);
  }

  async checkDependencyUpdates(projectPath: string): Promise<string> {
    const result = await this.executeMavenCommand(projectPath, 'versions:display-dependency-updates');
    
    return this.formatMavenOutput('依赖更新检查', result);
  }

  async analyzeDependencies(projectPath: string): Promise<string> {
    const result = await this.executeMavenCommand(projectPath, 'dependency:analyze');
    
    return this.formatMavenOutput('依赖分析', result);
  }

  async getProjectInfo(projectPath: string): Promise<MavenProjectInfo> {
    const pomPath = path.join(projectPath, 'pom.xml');
    
    if (!await fs.pathExists(pomPath)) {
      throw new Error('未找到pom.xml文件');
    }
    
    const pomContent = await fs.readFile(pomPath, 'utf-8');
    return this.parsePomFile(pomContent);
  }

  async validateProject(projectPath: string): Promise<{
    isValid: boolean;
    issues: string[];
    suggestions: string[];
  }> {
    const issues: string[] = [];
    const suggestions: string[] = [];
    
    try {
      // 检查pom.xml是否存在
      const pomPath = path.join(projectPath, 'pom.xml');
      if (!await fs.pathExists(pomPath)) {
        issues.push('未找到pom.xml文件');
        suggestions.push('请确保这是一个Maven项目');
        return { isValid: false, issues, suggestions };
      }
      
      // 验证pom.xml格式
      const result = await this.executeMavenCommand(projectPath, 'validate');
      
      if (!result.success) {
        issues.push('pom.xml格式验证失败');
        issues.push(result.error || result.output);
        suggestions.push('请检查pom.xml文件格式');
      }
      
      // 检查必要的目录结构
      const srcMainJava = path.join(projectPath, 'src', 'main', 'java');
      if (!await fs.pathExists(srcMainJava)) {
        issues.push('缺少src/main/java目录');
        suggestions.push('创建标准的Maven目录结构');
      }
      
      // 检查Maven版本兼容性
      const projectInfo = await this.getProjectInfo(projectPath);
      if (projectInfo.packaging === 'war' && !projectInfo.dependencies.some(dep => dep.artifactId.includes('servlet'))) {
        issues.push('WAR项目缺少Servlet依赖');
        suggestions.push('添加javax.servlet或jakarta.servlet依赖');
      }
      
    } catch (error) {
      issues.push(`验证过程中出错: ${error instanceof Error ? error.message : String(error)}`);
    }
    
    return {
      isValid: issues.length === 0,
      issues,
      suggestions
    };
  }

  private async executeMavenCommand(projectPath: string, command: string, timeout: number = 120000): Promise<MavenResult> {
    const startTime = Date.now();
    const fullCommand = `${this.mavenCmd} ${command}"`;
    
    try {
      const { stdout, stderr } = await execAsync(fullCommand, {
        cwd: projectPath,
        timeout,
        maxBuffer: 1024 * 1024 * 10 // 10MB buffer
      });
      
      const duration = Date.now() - startTime;
      
      return {
        success: true,
        output: stdout,
        error: stderr,
        duration,
        command: fullCommand
      };
    } catch (error: any) {
      const duration = Date.now() - startTime;
      
      return {
        success: false,
        output: error.stdout || '',
        error: error.stderr || error.message,
        duration,
        command: fullCommand
      };
    }
  }

  private formatMavenOutput(operation: string, result: MavenResult): string {
    const statusIcon = result.success ? '✅' : '❌';
    const duration = (result.duration / 1000).toFixed(2);
    
    let output = `${statusIcon} Maven ${operation} ${result.success ? '成功' : '失败'} (耗时: ${duration}秒)\n\n`;
    
    if (result.success) {
      // 提取关键成功信息
      const lines = result.output.split('\n');
      const importantLines = lines.filter(line => 
        line.includes('BUILD SUCCESS') ||
        line.includes('Total time:') ||
        line.includes('Finished at:') ||
        line.includes('Final Memory:') ||
        line.includes('Tests run:') ||
        line.includes('[INFO] Building')
      );
      
      if (importantLines.length > 0) {
        output += '关键信息:\n' + importantLines.join('\n') + '\n\n';
      }
    } else {
      // 提取错误信息
      output += '错误详情:\n';
      if (result.error) {
        output += result.error + '\n';
      }
      
      // 提取编译错误
      const lines = result.output.split('\n');
      const errorLines = lines.filter(line => 
        line.includes('[ERROR]') ||
        line.includes('COMPILATION ERROR') ||
        line.includes('Failed to execute goal')
      );
      
      if (errorLines.length > 0) {
        output += '\n编译错误:\n' + errorLines.join('\n') + '\n';
      }
    }
    
    return output;
  }

  private formatTestResults(result: MavenResult, testResults: any): string {
    let output = `❌ Maven 测试失败\n\n`;
    
    if (testResults.summary) {
      output += `测试结果汇总:\n${testResults.summary}\n\n`;
    }
    
    if (testResults.failures.length > 0) {
      output += `失败的测试:\n`;
      testResults.failures.forEach((failure: string, index: number) => {
        output += `${index + 1}. ${failure}\n`;
      });
      output += '\n';
    }
    
    if (testResults.errors.length > 0) {
      output += `测试错误:\n`;
      testResults.errors.forEach((error: string, index: number) => {
        output += `${index + 1}. ${error}\n`;
      });
    }
    
    return output;
  }

  private parseTestResults(output: string): {
    summary: string;
    failures: string[];
    errors: string[];
  } {
    const lines = output.split('\n');
    const failures: string[] = [];
    const errors: string[] = [];
    let summary = '';
    
    for (const line of lines) {
      if (line.includes('Tests run:')) {
        summary = line.trim();
      } else if (line.includes('FAILURE:') || line.includes('Failed:')) {
        failures.push(line.trim());
      } else if (line.includes('ERROR:') || line.includes('Error:')) {
        errors.push(line.trim());
      }
    }
    
    return { summary, failures, errors };
  }

  private async hasSpringBootPlugin(projectPath: string): Promise<boolean> {
    try {
      const pomPath = path.join(projectPath, 'pom.xml');
      const pomContent = await fs.readFile(pomPath, 'utf-8');
      return pomContent.includes('spring-boot-maven-plugin');
    } catch {
      return false;
    }
  }

  private parsePomFile(pomContent: string): MavenProjectInfo {
    const extractValue = (tag: string): string => {
      const match = pomContent.match(new RegExp(`<${tag}>([^<]+)</${tag}>`));
      return match ? match[1] : 'unknown';
    };
    
    // 提取基本信息
    const groupId = extractValue('groupId');
    const artifactId = extractValue('artifactId');
    const version = extractValue('version');
    const packaging = extractValue('packaging') || 'jar';
    
    // 提取依赖信息
    const dependencies: Array<{
      groupId: string;
      artifactId: string;
      version: string;
      scope?: string;
    }> = [];
    
    const dependenciesMatch = pomContent.match(/<dependencies>([\s\S]*?)<\/dependencies>/);
    if (dependenciesMatch) {
      const dependenciesContent = dependenciesMatch[1];
      const depMatches = dependenciesContent.matchAll(/<dependency>([\s\S]*?)<\/dependency>/g);
      
      for (const depMatch of depMatches) {
        const depContent = depMatch[1];
        const depGroupId = depContent.match(/<groupId>([^<]+)<\/groupId>/)?.[1] || '';
        const depArtifactId = depContent.match(/<artifactId>([^<]+)<\/artifactId>/)?.[1] || '';
        const depVersion = depContent.match(/<version>([^<]+)<\/version>/)?.[1] || '';
        const depScope = depContent.match(/<scope>([^<]+)<\/scope>/)?.[1];
        
        if (depGroupId && depArtifactId) {
          dependencies.push({
            groupId: depGroupId,
            artifactId: depArtifactId,
            version: depVersion,
            scope: depScope
          });
        }
      }
    }
    
    return {
      groupId,
      artifactId,
      version,
      packaging,
      dependencies
    };
  }
}